Go实战(五) 函数
函数声明
函数的声明形式如下:
func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) {
//这里是处理逻辑代码
//返回多个值
return value1, value2
}
其中, 函数返回的变量和类型中, 只写类型不写变量也是可以的. 如果返回值有多个, 需要用括号括起来, 只有一个则不用括号.
例子:
package main
import "fmt"
func max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
x := 3
y := 4
z := 5
maxxy := max(x, y)
maxxz := max(x, z)
fmt.Printf("max(%d, %d) = %d\n", x, y, maxxy)
fmt.Printf("max(%d, %d) = %d\n", x, z, maxxz)
}
多个返回值
package main
import "fmt"
func SumAndProduct(a, b int) (int, int) {
return a + b, a * b
}
func main() {
x := 10
y := 2
sum, product := SumAndProduct(x, y)
fmt.Printf("sum is %d\n", sum)
fmt.Printf("product is %d\n", product)
}
注意, 有多个返回值时, 函数声明时, 返回值列表要用括号括起来.
可变参数
拥有可变参数的函数声明形式:
func myFunc(arg...int) {}
例子:
package main
import "fmt"
func sum(vals ...int) int {
total := 0
for _, val := range vals {
total += val
}
return total
}
func main() {
fmt.Printf("sum is %d\n", sum(1, 2))
fmt.Printf("sum is %d\n", sum(1, 2, 3))
}
在函数体中, vals 是一个 slice.
传值与传指针
传值
Go 里面给函数传递参数的形式是值传递, 所以在函数内部对参数的修改, 不影响函数外部的值. 如:
package main
import "fmt"
func sum(a, b int) int {
a = a + 1
return a + b
}
func main() {
x := 2
y := 3
fmt.Printf("x is %d\n", x) // 2
total := sum(x, y)
fmt.Printf("total is %d\n", total) // 5
fmt.Printf("x is %d\n", x) // 2
}
可以看到, 虽然在函数 sum() 内部, 对第一个参数进行了加 1, 但是在函数外部, x 的值仍然是 2, 而不是 3. 因为在调用函数 sum() 时, 接收的参数其实是 x 的拷贝.
传指针
变量在内存中是存放在某个地址上的, 如果有这个地址, 就能修改这个值. 上面的例子中, 修改不了的原因, 是因为值一样, 但地址不同, 修改的是另一个地址的值. 例子如下:
package main
import "fmt"
func sum(a *int, b int) int {
*a = *a + 1
return *a + b
}
func main() {
x := 2
y := 3
fmt.Printf("x is %d\n", x) // 2
total := sum(&x, y)
fmt.Printf("total is %d\n", total) // 6
fmt.Printf("x is %d\n", x) // 3
}
可以看到, 通过传递指针, 实现了传递的数是同一个数.
传指针的好处很多. 如, 传指针可以提高程序的效率, 如果有一个体积很大的结构体, 传值相当于一次拷贝, 会浪费很多内存; 传指针可以使多个函数可以操作同一个对象.
Go 语言中的 channel, slice, map 的实现类似指针, 可以直接传递, 不用先取地址, 但是如果要改变 slice 的长度, 仍需先取地址.
defer
defer 是 Go 的关键字, defer 后面跟着的语句, 会在函数返回之前调用. 如:
func test() int {
fmt.Println("test 0")
defer fmt.Println("test 1")
fmt.Println("test 2")
return 0
}
// test 0
// test 2
// test 1
defer 常用于资源的释放. 如, 当打开某些资源时, 遇到错误要提前返回, 但是在返回前, 需要先关闭对应的资源, 否则会造成资源泄露等问题. 一般的写法如下:
func ReadWrite() bool {
file.Open("file")
// 做一些工作
if failureX {
file.Close()
return false
}
if failureY {
file.Close()
return false
}
file.Close()
return true
}
可以看出, 上面的程序里, 有很多重复的代码 file.Close(). 使用 defer 可以改善程序结构:
func ReadWrite() bool {
file.Open("file")
defer file.Close()
if failureX {
return false
}
if failureY {
return false
}
return true
}
如果有多个 defer 语句, 则后进先出. 如:
for i := 0; i < 6; i++ {
defer fmt.Println(i)
}
// 5 4 3 2 1 0
函数作为值和类型
在 Go 中, 函数也是一种变量, 可以通过 type 来定义它, 在定义时会写明参数和返回值, 如果某个函数的参数和返回值与它相同, 就是同一个类型. 如:
package main
import "fmt"
type testInt func(int) bool // 声明了一个函数类型
func isOdd(integer int) bool {
if integer%2 == 0 {
return false
}
return true
}
func isEven(integer int) bool {
if integer%2 == 0 {
return true
}
return false
}
// 声明的函数类型在这个地方当做了一个参数
func filter(slice []int, f testInt) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
return result
}
func main(){
slice := []int {1, 2, 3, 4, 5, 7}
fmt.Println("slice = ", slice)
odd := filter(slice, isOdd) // 函数当做值来传递了
fmt.Println("Odd elements of slice are: ", odd)
even := filter(slice, isEven) // 函数当做值来传递了
fmt.Println("Even elements of slice are: ", even)
}
函数的这种用法对于编写通用接口非常有用, 我们可以使用相同类型的参数和返回值, 编写不同的逻辑, 使得程序变得更加灵活.
Generated by Emacs 25.x(Org mode 8.x)
Copyright © 2014 - Pinvon - Powered by EGO